package com.fmy.security.config.filter;

import com.fmy.security.config.TokenProperties;
import com.fmy.security.service.login.CustAuthentication;
import com.fmy.security.service.login.CustAuthenticationHolder;
import com.fmy.security.service.login.authenticator.CustAuthenticator;
import org.springframework.context.ApplicationContext;
import org.springframework.http.HttpMethod;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.*;

/**
 * @author Mr.fmy
 * @version 1.0.0
 * @ClassName LoginTypeFilter.java
 * @Description TODO 登录类型处理filter
 * @createTime 2019年07月06日 15:25:00
 */
public class LoginTypeFilter extends OncePerRequestFilter {

    private String key = "login_type";

    private final Set<String> types;

    private AntPathRequestMatcher requestMatcher;

    private final String defultType;

    private Collection<CustAuthenticator> authenticators;

    private final ApplicationContext applicationContext;

    public LoginTypeFilter(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
        this.types = new HashSet<>();
        this.defultType = applicationContext.getBean(TokenProperties.class).getLoginTypeDefult();
        setRequestMatcher("/login/auth", HttpMethod.POST);
    }

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
        if (requiresAuthentication(request)) {
            try {
                String login_type = request.getParameter(key);
                if (!types.contains(login_type)) {
                    login_type = defultType;
                }
                CustAuthentication authentication = new CustAuthentication();
                authentication.setAuthType(login_type);
                authentication.setAuthParameters(request.getParameterMap());
                CustAuthenticationHolder.set(authentication);
                prepare(authentication);
                doFilter(request, response, filterChain);
            } finally {
                CustAuthenticationHolder.clear();
            }
        } else {
            doFilter(request, response, filterChain);
        }
    }

    public LoginTypeFilter setTypes(String... types) {
        this.types.addAll(Arrays.asList(types));
        return this;
    }

    public LoginTypeFilter setKey(String key) {
        if (key.isEmpty()) throw new IllegalArgumentException("login type key not empty");
        this.key = key;
        return this;
    }

    public LoginTypeFilter setRequestMatcher(String pattern, HttpMethod method) {
        this.requestMatcher = new AntPathRequestMatcher(pattern, method == null ? HttpMethod.POST.name() : method.name());
        return this;
    }

    private boolean requiresAuthentication(HttpServletRequest request) {
        return requestMatcher != null && requestMatcher.matches(request);
    }

    /**
     * 进行预处理
     */
    private void prepare(CustAuthentication authentication) {

        //延迟加载认证器
        if (this.authenticators == null) {
            synchronized (this) {
                Map<String, CustAuthenticator> authenticatorMap = applicationContext.getBeansOfType(CustAuthenticator.class);
                this.authenticators = authenticatorMap.values();
            }
        }
        for (CustAuthenticator item : authenticators) {
            if (item.support(authentication)) {
                item.prepare(authentication);
            }
        }
    }
}
